/**
 * Copyright &copy; 2016-2017 <a href="http://git.oschina.net/whoamien/backend_management">Backend Management</a> All rights reserved.
 */
package com.xw.util;

import java.nio.charset.StandardCharsets;
import java.security.DigestException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.shiro.codec.Base64;

/**
 * The Class CryptoJSAESUtil.
 *
 * @author Xi Wei
 */
public class CryptoJSAESUtil {

	/**
	 * Encrypt.
	 *
	 * @param plain the plain
	 * @param key the key
	 * @return the string
	 */
	public static String encrypt(String plain, String key) {
		try {
			byte[] cipherData = Base64.encode(plain.getBytes());
			byte[] saltData = Arrays.copyOfRange(Base64.encode(key.getBytes()), 8, 16);

			System.out.println("saltData=" + Base64.encodeToString(saltData));
			MessageDigest md5 = MessageDigest.getInstance("MD5");
			final byte[][] keyAndIV = GenerateKeyAndIV(32, 16, 1, saltData, key.getBytes(StandardCharsets.UTF_8), md5);
			SecretKeySpec k = new SecretKeySpec(keyAndIV[0], "AES");
			IvParameterSpec iv = new IvParameterSpec(keyAndIV[1]);
			System.out.println("key = " + Base64.encodeToString(keyAndIV[0]));
			System.out.println("IV = " + Base64.encodeToString(keyAndIV[1]));

			// byte[] e1 = Arrays.copyOfRange(cipherData, 16,
			// cipherData.length);
			Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");

			SecretKeySpec k1 = new SecretKeySpec(
					Base64.decode("0X4mXjTn8QszK1jG5IJcBevq4vABPtVQtIy8RCJcbws=".getBytes()), "AES");
			IvParameterSpec iv1 = new IvParameterSpec(Base64.decode("pBo0YpFxbGCgiK/eP6KCfA==".getBytes()));

			aesCBC.init(Cipher.ENCRYPT_MODE, k1, iv1);
			byte[] encryptedData = aesCBC.doFinal(plain.getBytes());
			String encryptedText = Base64.encodeToString(encryptedData);

			return encryptedText;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return "";
		}
	}

	/**
	 * Decrypt.
	 *
	 * @param encrypt the encrypt
	 * @param key the key
	 * @return the string
	 */
	public static String decrypt(String encrypt, String key) {
		try {
			byte[] cipherData = Base64.decode(encrypt);
			byte[] saltData = Arrays.copyOfRange(cipherData, 8, 16);

			MessageDigest md5 = MessageDigest.getInstance("MD5");
			final byte[][] keyAndIV = GenerateKeyAndIV(32, 16, 1, saltData, key.getBytes(StandardCharsets.UTF_8), md5);
			SecretKeySpec k = new SecretKeySpec(keyAndIV[0], "AES");
			IvParameterSpec iv = new IvParameterSpec(keyAndIV[1]);

			System.out.println("saltData=" + Base64.encodeToString(saltData));
			System.out.println("key = " + Base64.encodeToString(keyAndIV[0]));
			System.out.println("IV = " + Base64.encodeToString(keyAndIV[1]));

			byte[] e1 = Arrays.copyOfRange(cipherData, 16, cipherData.length);
			Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
			aesCBC.init(Cipher.DECRYPT_MODE, k, iv);
			byte[] decryptedData = aesCBC.doFinal(e1);
			String decryptedText = new String(decryptedData, StandardCharsets.UTF_8);

			aesCBC.init(Cipher.ENCRYPT_MODE, k, iv);
			String test = Base64.encodeToString(aesCBC.doFinal(decryptedData));
			System.out.println("test=" + test);
			;
			return decryptedText;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return "";
		}
	}

	/**
	 * Generate key and IV.
	 *
	 * @param keyLength the key length
	 * @param ivLength the iv length
	 * @param iterations the iterations
	 * @param salt the salt
	 * @param password the password
	 * @param md the md
	 * @return the byte[][]
	 */
	public static byte[][] GenerateKeyAndIV(int keyLength, int ivLength, int iterations, byte[] salt, byte[] password,
			MessageDigest md) {

		int digestLength = md.getDigestLength();
		int requiredLength = (keyLength + ivLength + digestLength - 1) / digestLength * digestLength;
		byte[] generatedData = new byte[requiredLength];
		int generatedLength = 0;

		try {
			md.reset();

			// Repeat process until sufficient data has been generated
			while (generatedLength < keyLength + ivLength) {

				// Digest data (last digest if available, password data, salt if
				// available)
				if (generatedLength > 0)
					md.update(generatedData, generatedLength - digestLength, digestLength);
				md.update(password);
				if (salt != null)
					md.update(salt, 0, 8);
				md.digest(generatedData, generatedLength, digestLength);

				// additional rounds
				for (int i = 1; i < iterations; i++) {
					md.update(generatedData, generatedLength, digestLength);
					md.digest(generatedData, generatedLength, digestLength);
				}

				generatedLength += digestLength;
			}

			// Copy key and IV into separate byte arrays
			byte[][] result = new byte[2][];
			result[0] = Arrays.copyOfRange(generatedData, 0, keyLength);
			if (ivLength > 0)
				result[1] = Arrays.copyOfRange(generatedData, keyLength, keyLength + ivLength);

			return result;

		} catch (DigestException e) {
			throw new RuntimeException(e);

		} finally {
			// Clean out temporary data
			Arrays.fill(generatedData, (byte) 0);
		}
	}

	/**
	 * The main method.
	 *
	 * @param args the arguments
	 */
	public static void main(String[] args) {

		// TODO Auto-generated method stub
		String plain = "The quick brown fox jumps over the lazy dog. 👻 👻";
		String key = "1d81f44b268e28fce1607a5c1d02b3b1adde60a24a9ec9492b7a5390e21b4830";
		String encrypted = encrypt(plain, key);
		System.out.println(encrypted);

		String encrypted1 = "U2FsdGVkX19qUbD2MH2rDSOIW0JPyq4wiCHsqFterq4lpDhuwIxD5mcCBrIzNhtOGRYtI6J5LgVptPcHx6dt4biIBDr59CuJ6bwfamSpowY=";
		String plain1 = decrypt(encrypted1, key);
		System.out.println(plain1);
	}

}
